package com.grape.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.grape.domain.Bill;
import com.grape.dto.*;
import com.grape.mapper.BillMapper;
import com.grape.service.IAccountService;
import com.grape.service.IBillService;
import com.grape.constants.Constants;
import com.grape.req.StatisticYearMonthReq;
import com.grape.utils.BigDecimalUtil;
import com.grape.utils.DateUtils;
import com.grape.utils.StringUtils;
import com.grape.utils.ThreadLocalUtil;
import com.grape.vo.BaseVo;
import com.grape.vo.DailyVO;
import com.grape.wrapper.BaseWrapper;
//import com.grape.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *
 * 账单表 服务实现类
 *
 * @author      putao
 * @email       2628279194@qq.com
 */
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class BillServiceImpl extends ServiceImpl<BillMapper, Bill> implements IBillService {

    private final BillMapper billMapper;

    private final IAccountService accountService;

//    private final ISysUserService userService;

    private static final Logger logger = org.slf4j.LoggerFactory.getLogger(BillServiceImpl.class);

    /**
     * 分页
     * @param baseVo    统一分页对象
     * @return          账单表集合
     */
    @Override
    public List<Bill> listData(BaseVo baseVo) {
        QueryWrapper<Bill> wrapper = new BaseWrapper<Bill>().init();
        wrapper.like(Strings.isNotBlank(baseVo.getSearch()), Constants.NAME, baseVo.getSearch())
                .eq(StringUtils.isNotBlank(baseVo.getColumnName()) && StringUtils.isNotBlank(baseVo.getSelect()), baseVo.getColumnName(), baseVo.getSelect());
        orderByModifyTimeDesc(wrapper);
        return this.list(wrapper);
    }

    /**
     * 自定义分页
     * @param   baseVo      统一分页对象
     */
    @Override
    public void pageListCustom(BaseVo baseVo) {
        String userId = ThreadLocalUtil.getUserId();
        QueryWrapper<Bill> qw = new QueryWrapper<Bill>().eq(Constants.USER_ID, userId)
                .orderByDesc(Constants.CREATE_TIME);
        this.billMapper.selectList(qw);
    }

    /**
     * 修改
     * @param bill 账单表对象
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void updateData(Bill bill) {
        List<String> accountIdList = new ArrayList<>();
        Bill old = this.getByIdForDelFlag(bill.getId());
        if (old != null && !old.getAccountId().equals(bill.getAccountId())) {
            accountIdList.add(old.getAccountId());
        }
        accountIdList.add(bill.getAccountId());
        bill.initProperties();
        this.baseUpdate(bill);
        this.afterOperationBill(accountIdList);
    }

    /**
     * 新增
     * @param       bill 账单表对象
     * @return      账单表对象
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public Bill insertData(Bill bill) {
        bill.initProperties();
        List<String> accountIdList = Collections.singletonList(bill.getAccountId());
        Bill insert = this.baseInsert(bill);
        this.afterOperationBill(accountIdList);
        return insert;
    }

    /**
     * 删除
     * @param id    单据id
     * @return      删除条数
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public int deleteData(String id) {
        Bill bill = this.detail(id);
        int count = this.baseDelete(id);
        this.afterOperationBill(Collections.singletonList(bill.getAccountId()));
        return count;
    }

    /**
     * 根据id查询详情
     * @param       id  单据id
     * @return      账单表对象
     */
    @Override
    public Bill detail(String id) {
        return this.getByIdForDelFlag(id);
    }

    /**
     * 账单操作后，更新账户和用户信息
     * @param accountIdList 账户id集合
     */
    private void afterOperationBill(List<String> accountIdList) {
        accountService.updateMoney(this.getBillList(accountIdList), accountIdList);
        List<Bill> billList = this.getBillList(null);
        // TODO
//        userService.updateBookkeepingNumber(billList == null ? 0 : billList.size());
    }

    /**
     * 根据账户id查询全部账单
     * @param accountIdList 账户id集合
     * @return  账户下所有的账单
     */
    private List<Bill> getBillList(List<String> accountIdList) {
        return this.list(new BaseWrapper<Bill>().initDelFlagAndUserId().in(CollectionUtil.isNotEmpty(accountIdList), Constants.ACCOUNT_ID, accountIdList));
    }

    /**
     * 统计支出
     * @param startDay  开始时间
     * @param endDay    结束时间
     * @return  支出统计信息
     */
    @Override
    public List<LineDTO> getTotal(String startDay, String endDay) {
        return billMapper.getTotal(startDay, endDay, ThreadLocalUtil.getUserId());
    }

    /**
     * 按天查询统计账单
     * @param dailyVO   时间
     * @return 统计账单
     */
    @Override
    public List<DailyDTO> dailyDTOList(DailyVO dailyVO) {
        dailyVO.setUserId(ThreadLocalUtil.getUserId());
        List<BillDTO> billDTOList = billMapper.getBillDTOList(dailyVO);
        if (CollectionUtil.isEmpty(billDTOList)) {
            return Collections.emptyList();
        }
        Map<String, List<BillDTO>> dtoList = billDTOList.stream().collect(Collectors.groupingBy(BillDTO::getDayString));
        List<DailyDTO> dailyDTOList = new ArrayList<>();
        for (Map.Entry<String, List<BillDTO>> entry : dtoList.entrySet()) {
            List<BillDTO> list = entry.getValue();
            DailyDTO dailyDTO = new DailyDTO();
            BillDTO billDTO = list.get(0);
            dailyDTO.setDay(billDTO.getDay());
            dailyDTO.setWeek(billDTO.getWeek());
            dailyDTO.setDayString(billDTO.getDayString());
            dailyDTO.setDayTimestamp(billDTO.getDayTimestamp());
            dailyDTO.setBillDTOList(list.stream().sorted(Comparator.comparing(BillDTO::getTime).reversed()).collect(Collectors.toList()));
            BigDecimal income = BigDecimal.ZERO;
            BigDecimal expenses = BigDecimal.ZERO;
            for (BillDTO dto : list) {
                if (Constants.CATEGORY_TYPE_INCOME.equals(dto.getCategoryType())) {
                    income = BigDecimalUtil.add(income, BigDecimal.valueOf(dto.getMoney()));
                }
                if (Constants.CATEGORY_TYPE_PAY.equals(dto.getCategoryType())) {
                    expenses = BigDecimalUtil.add(expenses, BigDecimal.valueOf(dto.getMoney()));
                }
            }
            dailyDTO.setExpenses(BigDecimalUtil.getDoubleValue(expenses));
            dailyDTO.setIncome(BigDecimalUtil.getDoubleValue(income));
            dailyDTOList.add(dailyDTO);
        }
        return dailyDTOList.stream().sorted(Comparator.comparingLong(DailyDTO::getDayTimestamp).reversed()).collect(Collectors.toList());
    }

    /**
     * 获取当前月统计账单
     * @param accountBookId 账本id
     * @return 月份统计信息
     */
    @Override
    public StatisticalCurrentMonthDTO getCurrentMonthStatistical(String accountBookId) {
        Date date = new Date();
        StatisticalCurrentMonthDTO currentMonthStatistical = billMapper.getCurrentMonthStatistical(DateUtils.getFirstDayOfMonth(date), DateUtils.getNextMonthFirstDay(date), ThreadLocalUtil.getUserId(), accountBookId);
        currentMonthStatistical.initBalance();
        return currentMonthStatistical;
    }

    /**
     * 获取前6个月统计数据
     * @param accountBookId 账本id
     * @param type  类型（pay: 支出，income：收入）
     * @return 统计数据
     */
    @Override
    public StatisticalMonthChartDTO statisticMonthChart(String accountBookId, String type) {
        String startDay = DateUtils.getFirstDayOfRelativeMonth(-11);
        String endDay = DateUtils.getLastDayOfRelativeMonth(0);
        List<MonthMoneyDTO> monthMoneyDTOList = billMapper.statisticMonthChart(accountBookId, type, startDay, endDay, ThreadLocalUtil.getUserId());
        List<MonthChartDTO> chartDTOList = this.generateLastMonths(12);
        Map<String, MonthMoneyDTO> moneyDTOMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(monthMoneyDTOList)) {
            moneyDTOMap = monthMoneyDTOList.stream().collect(Collectors.toMap(MonthMoneyDTO::getMonth, Function.identity(), (v1, v2) -> v1));
        }
        Map<String, MonthChartDTO> horizontalAxisData = new HashMap<>();
        List<Double> longitudinalData = new ArrayList<>();
        int index = 1;
        BigDecimal max = BigDecimal.ZERO;
        for (MonthChartDTO chartDTO : chartDTOList) {
            MonthMoneyDTO dto = moneyDTOMap.get(chartDTO.getYear() + Constants.H + chartDTO.getMonth());
            horizontalAxisData.put(index + StringUtils.EMPTY, chartDTO);
            index = index + 2;
            BigDecimal money = dto != null ? dto.getMoney() : BigDecimal.ZERO;
            longitudinalData.add(BigDecimalUtil.getDoubleValue(money));
            max = BigDecimalUtil.biggerThen(money, max) ? money : max;
        }

        StatisticalMonthChartDTO statisticalMonthChartDTO = new StatisticalMonthChartDTO();
        statisticalMonthChartDTO.setHorizontalAxisData(horizontalAxisData);
        statisticalMonthChartDTO.setAxisData(BigDecimalUtil.getNumberList(max.intValue()));
        statisticalMonthChartDTO.setLongitudinalData(longitudinalData);
        return statisticalMonthChartDTO;
    }

    /**
     * 生成最近7个月的月份数据
     * @return 最近7个月的月份数据列表
     */
    public static List<MonthChartDTO> generateLastMonths(int months) {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy");
        SimpleDateFormat sdfMonth = new SimpleDateFormat("M");
        List<MonthChartDTO> chartDTOList = new ArrayList<>();
        calendar.add(Calendar.MONTH, -months + 1);
        // 循环生成前7个月的月份数据，包括当前月份
        for (int i = 1; i <= months; i++) {
            String year = sdfYear.format(calendar.getTime());
            String month = sdfMonth.format(calendar.getTime());
            MonthChartDTO chartDTO = new MonthChartDTO();
            chartDTO.setMonth(month);
            chartDTO.setYear(year);
            chartDTOList.add(chartDTO);
            // 回退一个月
            calendar.add(Calendar.MONTH, 1);
        }
        return chartDTOList;
    }

    /**
     * 获取指定年月的统计数据
     * @param statisticYearMonthReq  账本id，年，月，类型
     * @return 统计数据
     */
    @Override
    public StatisticalMonthDTO statisticByYearMonth(StatisticYearMonthReq statisticYearMonthReq) {
        statisticYearMonthReq.setUserId(ThreadLocalUtil.getUserId());
        statisticYearMonthReq.setStartDay(DateUtils.getFirstDayOfMonth(statisticYearMonthReq.getYear(), statisticYearMonthReq.getMonth()));
        statisticYearMonthReq.setEndDay(DateUtils.getNextMonthFirstDay(statisticYearMonthReq.getYear(), statisticYearMonthReq.getMonth()));
        StatisticalMonthDTO data = billMapper.statisticByYearMonth(statisticYearMonthReq);
        StatisticalMonthDTO dto = StatisticalMonthDTO.createByRequest(statisticYearMonthReq);
        dto.setTotalMoney(data.getTotalMoney());
        dto.setTotalNumber(data.getTotalNumber());
        dto.initAveragePerDay();

        ArrayList<CategoryRatioDTO> categoryRatioDTOList = new ArrayList<>();
        dto.setCategoryRatioDTOList(categoryRatioDTOList);

        ArrayList<CategoryMoneyDTO> categoryMoneyDTOList = new ArrayList<>();
        dto.setCategoryMoneyDTOList(categoryMoneyDTOList);

        List<CategoryDataDTO> categoryDataDTOList = billMapper.statisticCategoryByYearMonth(statisticYearMonthReq);
        if (CollectionUtil.isNotEmpty(categoryDataDTOList)) {

            BigDecimal total = BigDecimal.ZERO;

            for (CategoryDataDTO categoryDataDTO : categoryDataDTOList) {
                total = BigDecimalUtil.add(total, categoryDataDTO.getMoney());
                CategoryRatioDTO ratioDTO = new CategoryRatioDTO();
                ratioDTO.setName(categoryDataDTO.getName());
                ratioDTO.setMoney(categoryDataDTO.getMoney());
                categoryRatioDTOList.add(ratioDTO);

                CategoryMoneyDTO moneyDTO = new CategoryMoneyDTO();
                moneyDTO.setId(categoryDataDTO.getId());
                moneyDTO.setAppIcon(categoryDataDTO.getAppIcon());
                moneyDTO.setName(categoryDataDTO.getName());
                moneyDTO.setMoney(BigDecimalUtil.getDoubleValue(categoryDataDTO.getMoney()));
                moneyDTO.setBudget(categoryDataDTO.getBudget());
                categoryMoneyDTOList.add(moneyDTO);
            }

            for (CategoryRatioDTO categoryRatioDTO : categoryRatioDTOList) {
                categoryRatioDTO.setRatio(BigDecimalUtil.mul(BigDecimalUtil.div(categoryRatioDTO.getMoney(), total), BigDecimalUtil.ONE_HUNDRED).setScale(0, RoundingMode.HALF_UP).doubleValue());
            }

            for (CategoryMoneyDTO categoryMoneyDTO : categoryMoneyDTOList) {
                categoryMoneyDTO.initRatio(total, statisticYearMonthReq.getType());
            }
        }

        return dto;
    }
}
